package main

import (
	"fmt"
	"math"
	"math/rand"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"
)

type triangle struct { //三角形结构体
	height float64
	button float64
}

func (t *triangle) setHeight(h float64) { //设置三角形的高
	t.height = h
}

func (t *triangle) getHeight() float64 { //获取三角形的高
	return t.height
}

func (t *triangle) setButton(b float64) { //设置三角形的底
	t.button = b
}

func (t *triangle) getButton() float64 { //获取三角形的底
	return t.button
}

func (t *triangle) square() float64 { //三角形结构体实现square()方法
	return t.height * t.button / 2
}

type rectangle struct { //长方形结构体
	long  float64
	width float64
}

func (r *rectangle) setLong(l float64) { //设置长方形的长
	r.long = l
}

func (r *rectangle) getLong() float64 { //获取长方形的长
	return r.long
}

func (r *rectangle) setWidth(w float64) { //设置长方形的宽
	r.width = w
}

func (r *rectangle) getWidth() float64 { //获取长方形的宽
	return r.width
}

func (r *rectangle) square() float64 { //三角形结构体实现square()方法
	return r.long * r.width
}

type circle struct { //圆形结构体
	radius float64
}

func (c *circle) setRadius(r float64) { //设置圆形半径
	c.radius = r
}

func (c *circle) getRadius() float64 { //获取圆形半径
	return c.radius
}

func (c *circle) square() float64 { //圆形结构体实现square()方法
	return math.Pow(c.radius, 2) * math.Pi
}

type shape interface { //形状结构体interface
	square() float64
}

func squares(s shape) float64 { //多态实现调用所有计算面积的实现
	return s.square()
}

func RoundWithPrecision(f float64, precision int) float64 { //按float精度四舍五入
	if precision == 0 {
		return math.Round(f)
	}
	p := math.Pow10(precision)
	if p < 0 {
		return math.Round(f*p) * math.Pow10(-precision)
	}
	return math.Round(f*p) / p
}

func NewGenerate(number int) int { //随机数共用方法
	source := rand.NewSource(time.Now().UnixNano())
	gen := rand.New(source)
	return gen.Intn(number)
}

func initShape(n int) map[string]float64 { //初始化三角形/长方形/圆形的map数据
	var sortMap = make(map[string]float64)
	if n == 0 {
		return sortMap
	}
	number := n
	for i := 0; i < number; i++ {
		n := NewGenerate(i + 1)
		var s shape
		switch n {
		case 0: //三角形
			t := new(triangle)
			t.setHeight(float64(NewGenerate(10+i+n) + 10))
			t.setButton(float64(NewGenerate(6+i+n) + 6))
			s = t
			sortMap["triangole"+strconv.Itoa(i+1)] = RoundWithPrecision(squares(s), 2) //使用多态实现
		case 1: //长方形
			r := new(rectangle)
			r.setLong(float64(NewGenerate(10+i+n) + 10))
			r.setWidth(float64(NewGenerate(4+i+n) + 4))
			s = r
			sortMap["rectangle"+strconv.Itoa(i+1)] = RoundWithPrecision(squares(s), 2) //使用多态实现
		default: //默认圆形
			c := new(circle)
			c.setRadius(float64(NewGenerate(5+i+n) + 5))
			s = c
			sortMap["circle"+strconv.Itoa(i+1)] = RoundWithPrecision(squares(s), 2) //使用多态实现
		}
	}
	return sortMap
}

func main() {
	//作业1
	number := 8
	sortMap := initShape(number) //初始化多个形状并获取它们的面积，组成一个map
	fmt.Printf("init shape square map data is : %#v\n", sortMap)

	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

	//作业2
	if len(sortMap) > 0 {
		sortByKey(sortMap) //map中使用key进行排序
		fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
		sortByValue(sortMap) //map中使用value进行排序
	}

	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

	//作业3
	num := 11
	fibonacciNum := fibonacciRecurrence(num) //优化斐波那契数列的递归写法，使用字典map存储递归时已经计算过的递归结果，避免重复计算
	fmt.Printf("the No. %d fibonacci number is : %d\n", num, fibonacciNum)

	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

	//作业4
	//获取8年前9:30的时间字符串
	newTime := createNewTime() //拼字符串
	// newTime := createNewTime1()	//字符串替换
	fmt.Println(newTime)
	newTime1 := newTime.Format("2006/01/02 15:04:05 -0700")
	fmt.Printf("output format datetime string is : %s\n", newTime1)                                 //2006/01/02 15:04:05 -0700格式化输出
	fmt.Printf("millisecond of datetime is : %d\n", newTime.UnixMilli())                            //8年前今天9:30的毫秒
	fmt.Printf("the weekday  of datetime is : %d\n", newTime.Weekday())                             //8年前今天9:30的周几，数字
	fmt.Printf("the weekday string of datetime is : %s\n", newTime.Weekday().String())              //8年前今天9:30的周几，字符串
	fmt.Printf("days that cost in datetime year is : %d\n", newTime.YearDay())                      //8年前今天9:30当年经过的天数
	fmt.Printf("weeks that cost in datetime year is : %d\n", newTime.YearDay()/7)                   //8年前今天9:30当年经过的星期数
	fmt.Printf("days that cost until now is : %d\n", (time.Now().Unix()-newTime.Unix())/(24*60*60)) //8年前今天到现在经过的天数
}

var fibonacciExistsMap = make(map[int]int) //创建全局字典map，用来保存递归斐波那契已经计算过的值

func fibonacciRecurrence(n int) int {
	switch {
	case n < 0:
		return 0
	case n < 3:
		return 1
	}
	fibonacci1, ok := fibonacciExistsMap[n-1] //获取n-1个斐波那契数
	if !ok {
		fibonacci1 = fibonacciRecurrence(n - 1) //如果n-1个斐波那契数不存在，计算并存入全局字典
		fibonacciExistsMap[n-1] = fibonacci1
	}
	fibonacci2, ok := fibonacciExistsMap[n-2] //获取n-2个斐波那契数
	if !ok {
		fibonacci2 = fibonacciRecurrence(n - 2) //如果n-2个斐波那契数不存在，计算并存入全局字典
		fibonacciExistsMap[n-2] = fibonacci2
	}
	return fibonacci1 + fibonacci2
}

func createNewTime() time.Time { //通过字符串方式获取8年前今日9:30的时间
	current := time.Now() //获取当前时间对象

	fmt.Println(current)

	//方法1
	currentYear := current.Year()
	//拼接新日期的字符串
	builder := new(strings.Builder)
	builder.WriteString(fmt.Sprintf("%d", currentYear-8))
	builder = formatTimeString(current, 1, builder)
	builder.WriteString(fmt.Sprintf(" %d:%d:%d", 9, 30, current.Second()))

	loc, err := time.LoadLocation("Asia/Shanghai") //加载当地的时区
	defer func() {
		e := recover()
		fmt.Println(e)
		switch v := e.(type) {
		case runtime.Error:
			fmt.Println(v)
		}
	}()
	if err != nil {
		panic(err)
	}
	new, err := time.ParseInLocation("2006-01-02 15:04:05", builder.String(), loc) //根据格式将字符串格式化成以当地时区为基础的时间对象
	if err != nil {
		panic(err)
	}
	return new
}

func createNewTime1() time.Time {
	current := time.Now()
	newTime := current.AddDate(-8, 0, 0)
	newTimeString := newTime.Format("2006-01-02 15:04:05.9 -0700")
	builder := new(strings.Builder)
	builder = formatTimeString(current, 2, builder)
	builder.WriteString(":")
	timeString := strings.ReplaceAll(newTimeString, builder.String(), "09:30:")
	loc, err := time.LoadLocation("Asia/Chongqing")
	defer func() {
		e := recover()
		switch v := e.(type) {
		case runtime.Error:
			fmt.Println(v)
		}
	}()
	if err != nil {
		panic(err)
	}
	fmt.Println(timeString)
	new, err := time.ParseInLocation("2006-01-02 15:04:05.9 -0700", timeString, loc)
	if err != nil {
		panic(err)
	}
	return new
}

func formatTimeString(current time.Time, t int, builder *strings.Builder) *strings.Builder {
	switch t {
	case 1:
		if current.Month() < 10 {
			builder.WriteString(fmt.Sprintf("-0%d", current.Month()))
		} else {
			builder.WriteString(fmt.Sprintf("-%d", current.Month()))
		}
		if current.Day() < 10 {
			builder.WriteString(fmt.Sprintf("-0%d", current.Day()))
		} else {
			builder.WriteString(fmt.Sprintf("-%d", current.Day()))
		}
	case 2:
		if current.Hour() < 10 {
			builder.WriteString(fmt.Sprintf("0%d", current.Hour()))
		} else {
			builder.WriteString(fmt.Sprintf("%d", current.Hour()))
		}
		if current.Minute() < 10 {
			builder.WriteString(fmt.Sprintf(":0%d", current.Minute()))
		} else {
			builder.WriteString(fmt.Sprintf(":%d", current.Minute()))
		}
	}
	return builder
}

func sortByKey(m map[string]float64) { //map中使用key进行排序
	slice := make([]string, 0, len(m))
	for k, _ := range m {
		slice = append(slice, k)
	}

	sort.Slice(slice, func(i, j int) bool {
		return slice[i] > slice[j]
	})
	for _, v := range slice {
		fmt.Printf("map that sortBy key element is : %s ===> %.2f\n", v, m[v])
	}
}

type sortSturct struct { //值排序结构体
	key   string
	value float64
}

func sortByValue(m map[string]float64) { //map中使用value进行排序
	slice := make([]sortSturct, 0, len(m))

	for k, v := range m {
		sorts := sortSturct{k, v}
		slice = append(slice, sorts)
	}
	sort.Slice(slice, func(i, j int) bool {
		return slice[i].value > slice[j].value
	})

	for _, v := range slice {
		fmt.Printf("map that sortBy value element is : %s ===> %.2f\n", v.key, v.value)
	}
	fmt.Println(slice)
}

// 阿飞老师的批改
// 实现逻辑没有问题，时间字符串格式化部分通过自己实现的比较厉害，
// 但是在做业务的时候可以尽量使用官方提供的库函数，提高自己的工作效率
